package com.caowei.weicimpress.weicimpress;

import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.os.Build;
import android.os.Environment;
import android.os.Process;
import android.os.SystemClock;
import android.util.Log;

import com.caowei.weicimpress.weicimpress.widget.Toast;

import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.io.Writer;
import java.lang.reflect.Field;
import java.util.Calendar;
import java.util.HashMap;
import java.util.Map;

/**
 * Created by caoweiPc on 2014/7/23.
 */
public class ThreadUncaughtExceptionHandler implements Thread.UncaughtExceptionHandler {
    public static final String TAG=ThreadUncaughtExceptionHandler.class.getSimpleName();
    Thread.UncaughtExceptionHandler mDefaultHandler;
    private static ThreadUncaughtExceptionHandler INSTANCE;
    private Context mContext;
    private Map<String, String> infos = new HashMap<String, String>();
    private ThreadUncaughtExceptionHandler() {

    }
    public static ThreadUncaughtExceptionHandler getInstance() {
        if(INSTANCE==null){
            INSTANCE=new ThreadUncaughtExceptionHandler();
        }
        return INSTANCE;
    }

    public void init(Context context) {
        mContext=context;
        mDefaultHandler=Thread.getDefaultUncaughtExceptionHandler();
        Thread.setDefaultUncaughtExceptionHandler(this);
    }

    @Override
    public void uncaughtException(Thread thread, Throwable ex) {
        if(!handleException(ex)&&mDefaultHandler!=null){
            mDefaultHandler.uncaughtException(thread, ex);
        }else{
            SystemClock.sleep(3000);
            android.os.Process.killProcess(Process.myPid());
            Utils.exit(1);
        }
    }

    private boolean handleException(Throwable ex) {
        if(ex==null){
            return false;
        }
        Toast.makeText(mContext, "很抱歉,程序出现异常,即将退出.", Toast.LENGTH_SHORT).show();
        collectDeviceInfo(mContext);
        handleCrashInfo(ex);
        return true;
    }

    private String handleCrashInfo(Throwable ex) {
        StringBuilder sb = new StringBuilder();
        for (Map.Entry<String, String> entry : infos.entrySet()) {
            String key = entry.getKey();
            String value = entry.getValue();
            sb.append(key + "=" + value + "\n");
        }
        Writer writer=new StringWriter();
        PrintWriter printWriter = new PrintWriter(writer);
        ex.printStackTrace();
        ex.printStackTrace(printWriter);
        Throwable cause = ex.getCause();
        while (cause != null) {
            cause.printStackTrace(printWriter);
            cause = cause.getCause();
        }
        printWriter.close();
        String result = writer.toString();
        sb.append(result);

        BufferedOutputStream bfo=null;
        try{
            long timestamp=System.currentTimeMillis();
            String time = Utils.formateDate(Calendar.getInstance());

            ApplicationInfo info= mContext.getApplicationInfo();

            String fileName = "-crash-" + time + "-" + timestamp + ".log";
            StringBuilder label= new StringBuilder(info.loadLabel(mContext.getPackageManager()));
            fileName=label.append(fileName).toString();

            if(Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)){
                //todo 错误保存目录 以后改

                File crashDir=null;
                if(Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)){
                    crashDir=mContext.getExternalFilesDir(null);
                }else{
                    crashDir=mContext.getFilesDir();
                }
                crashDir=new File(crashDir,"crash");
                if(!crashDir.exists()){
                    crashDir.mkdirs();
                }
                File file=new File(crashDir,fileName);
                bfo=new BufferedOutputStream(new FileOutputStream(file));
                bfo.write(sb.toString().getBytes());
                bfo.flush();
                sendCrashInfoByEmail();
            }
        } catch (Throwable e) {
            e.printStackTrace();
            Log.e(TAG, "an error occured while writing file...", e);
        } finally {
            if(bfo!=null){
                try {
                    bfo.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return sb.toString();
    }

    private void sendCrashInfoByEmail() {
        //todo 检测网络   发送出错信息去 邮箱
    }

    private void collectDeviceInfo(Context ctx) {
        try{
            PackageManager pm=ctx.getPackageManager();
            PackageInfo pi=pm.getPackageInfo(ctx.getPackageName(),PackageManager.GET_ACTIVITIES);
            if(pi!=null){
                String versionName = pi.versionName == null ? "null" : pi.versionName;
                String versionCode = pi.versionCode + "";
                infos.put("versionName", versionName);
                infos.put("versionCode", versionCode);
            }
        } catch (PackageManager.NameNotFoundException e) {
            e.printStackTrace();
            Log.e(TAG, "an error occured when collect package info", e);
        } finally {

        }
        Field[] fields= Build.class.getDeclaredFields();
        for(Field field:fields){
            field.setAccessible(true);
            try {
                infos.put(field.getName(), field.get(null).toString());
                Log.d(TAG, field.getName() + " : " + field.get(null));
            } catch (IllegalAccessException e) {
                e.printStackTrace();
                Log.e(TAG, "an error occured when collect crash info", e);
            }
        }
    }
}
